#include <sys/types.h>                                                                  // Definicao de tipos
#include <sys/socket.h>                                                                 // Biblioteca de estrutara de sockets
#include <netinet/in.h>                                                                 // Define os padroes de protocolo IP
#include <arpa/inet.h>                                                                  // Converte enderecos hosts
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>                                                                     // Define constantes POSIX
#include <errno.h>                                                                      // Utilizado para capturar mensagens de erro

#define TAM 500

void concatena (int a[], int b[], int c[], int T)                                     //função que une os 2 vetores recebidos doo nivel inferior
{
    int i;
    for (i=0; i<T; i++)
    {
        a[i] = b[i];
        a[T+i] = c[i];
    }
}

void quickSort( int a[], int l, int r)                                                  //quick sort recursiva
{
    int j;

    if( l < r )
    {
        j = partition( a, l, r);
        quickSort( a, l, j-1);                                                          //chamadas recursivas
        quickSort( a, j+1, r);
    }

}

int partition( int a[], int l, int r)                                                   //divisão do vetor para ordenação
{
    int pivot, i, j, t;
    pivot = a[l];
    i = l;
    j = r+1;

    while( 1)
    {
        do ++i;
        while( a[i] <= pivot && i <= r );
        do --j;
        while( a[j] > pivot );
        if( i >= j ) break;
        t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
    t = a[l];
    a[l] = a[j];
    a[j] = t;
    return j;
}

int main(int argc, char *argv[ ])
{
    int TAM = atoi(argv[1]);
    int sock, connected[2], i, true = 1, recv_data[2][TAM];                             //variaveis necessarias
    int active = 0, concatenado[TAM*2];
    struct sockaddr_in server_addr, client_addr[2];
    int sin_size;
    struct timeval Tini, Tfinal;
    double tempo;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)                                 //cria socket
    {
        perror("Erro no Socket");
        exit(1);
    }

    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true,sizeof(int)) == -1)            //muda config para server aceitar mais de uma
    {                                                                                   //conexão ao mesmo tempo
        perror("Erro Setsockopt");
        exit(1);
    }

    server_addr.sin_family = AF_INET;                                                   //Configura o endereco de destino
    server_addr.sin_port = htons(3792);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero),8);


    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)     //passa endereço do server as socket criado
    {
        perror("Nao foi possivel realizar o bind");
        exit(1);
    }

    if (listen(sock, 10) == -1)                                                         //mantém server aguardando conexoes
    {
        perror("Erro de Listen");
        exit(1);
    }

    printf("\nServidor TCP esperando por conexoes na porta 3792\n");
    fflush(stdout);

    for (active = 0 ; active<2; active++)                                               //Loop para receber as 2 conexões desejadas
    {
        sin_size = sizeof(struct sockaddr_in);                                          //armazena o tamanho de endereco do cliente

        connected[active] = accept(sock, (struct sockaddr *)&client_addr[active], &sin_size); //aceita a conexão e cria socket para client
        printf("\nConexão aceita de (%s , %d)\n", inet_ntoa(client_addr[active].sin_addr), ntohs(client_addr[active].sin_port));

        printf("recebendo dados\n");
        recv(connected[active], recv_data[active], TAM*4, 0);                           //recebe o vetor a ser ordenado

        bzero(&(server_addr.sin_zero),8);                                               //zera adrress para a proxima conexão
    }

    concatena(concatenado,recv_data[0],recv_data[1], TAM);                              //chama a função da concatenação de vetores

    gettimeofday(&Tini, NULL);                                                          //marca o tempo incial
    quickSort(concatenado, 0, (TAM*2)-1);                                               //chama a ordenação
    gettimeofday(&Tfinal, NULL);                                                        //fim do tempo de ordenação
    tempo = Tfinal.tv_sec+(Tfinal.tv_usec/1000000.0) - Tini.tv_sec+(Tini.tv_usec/1000000.0);

    printf("ordenado depois de %.8lf segundos", tempo);

    send(connected[0], concatenado, TAM*8, 0);                                          //envia o vetor final ordenado para o nivel inferior
    send(connected[1], concatenado, TAM*8, 0);

    close(sock);                                                                        //fecha socket com os niveis inferiores
    return 0;
}
